第一节 channel简单介绍

视频学习地址:
https://www.bilibili.com/video/BV17a41177Nr?spm_id_from=333.999.0.0&vd_source=a68414cd60fe26e829ce1cdd4d75a9e6

原文:
https://www.yuque.com/innodb-rjzvt/cg0xxl

1、无缓冲channel

ch := make(chan int, 0) //第二个参数为0,或者不写第二个参数

解释:是指在接收前没有能力保存任何值得通道。
这种类型的通道要求发送goroutine和接收goroutine同时准备好,才能完成发送和接收操作。如果两个goroutine没有同时准备好,通道会导致先执行发送或接收操作的goroutine阻塞等待。
这种对通道进行发送和接收的交互行为本身就是同步的,其中任意一个操作都无法离开另一个操作单独存在。


无缓冲通道

package main

import (
    "fmt"
    "time"
)

func main() {
    //创建一个无缓存的channel
    ch := make(chan int, 0)

    //len(ch)缓冲区剩余数据个数, cap(ch)缓冲区大小,两者这里永远都是0
    fmt.Printf("len(ch) = %d, cap(ch)= %d\n", len(ch), cap(ch))

    //新建协程
    go func() {
        for i := 0; i < 3; i++ { //写三次
            fmt.Printf("子协程:i = %d\n", i)
            ch <- i //往chan写内容
            fmt.Printf("len(ch) = %d, cap(ch)= %d\n", len(ch), cap(ch))
        }
    }()

    //延时秒数
    time.Sleep(5 * time.Second)

    for i := 0; i < 3; i++ { //必须读三次
        num := <-ch //读管道中内容,没有内容前,阻塞
        fmt.Println("num = ", num)
    }

}

2、有缓冲通道channel

ch := make(chan int, 1)

解释:指通道可以保存多个值,如果给定了一个缓冲区容量,那么通道就是异步的,只要缓冲区有未使用空间用于发送数据,或还包含可以接收的数据,那么其通信就会无阻塞地进行


有缓冲通道

package main

import (
    "fmt"
    "time"
)

func main() {
    //创建一个有缓存的channel
    ch := make(chan int, 3) //容量是3

    //len(ch)缓冲区剩余数据个数, cap(ch)缓冲区大小
    fmt.Printf("len(ch) = %d, cap(ch)= %d\n", len(ch), cap(ch))

    //新建协程
    go func() {
        for i := 0; i < 10; i++ { //这里数据量大于管道容量,会出阻塞
            ch <- i //往chan写内容,如果主协程没读的话,写满3个就会阻塞在此
            time.Sleep(2 * time.Second)
            fmt.Printf("子协程[%d]: len(ch) = %d, cap(ch)= %d\n", i, len(ch), cap(ch))
        }
    }()

    //延时
    //time.Sleep(10 * time.Second)

    for i := 0; i < 10; i++ { //这里数据量大于管道容量,会出阻塞
        num := <-ch //读管道中内容,没有内容前,阻塞
        fmt.Println("num = ", num)
    }

}

3.读取通道

3.1 读取方式一

package main

import "fmt"

func main() {
    c := make(chan int)

    go func() {
        for i := 0; i < 5; i++ {
            c <- i
            //close可以关闭一个channel
            close(c)
        }
    }()

    //方式一
    for {
        //ok如果为true表示channel没有关闭,如果为false表示channel已经关闭
        if data, ok := <-c; ok {
            fmt.Println(data)
        } else {
            break
        }
    }
    fmt.Println("Main Finished..")
}

3.2 读取方式二

package main

import "fmt"

func main() {
    c := make(chan int)

    go func() {
        for i := 0; i < 5; i++ {
            c <- i
        }

        //close可以关闭一个channel
        close(c)
    }()

    //可以使用range来迭代不断操作channel
    for data := range c {
        fmt.Println(data)
    }

    fmt.Println("Main Finished..")
}

3.3 读取方式三

package main

import "fmt"

func fibonacii(c, quit chan int) {
    x, y := 1, 1

    for {
        select {
        case c <- x:
            //如果c可写,则该case就会进来
            x = y
            y = x + y
        case <-quit:
            fmt.Println("quit")
            return
        }
    }
}

func main() {
    c := make(chan int)
    quit := make(chan int)

    //sub go
    go func() {
        for i := 0; i < 10; i++ {
            fmt.Println(<-c)
        }

        quit <- 0
    }()

    //main go
    fibonacii(c, quit)
}
作者:admin  创建时间:2022-07-05 23:40
最后编辑:海马  更新时间:2024-05-10 15:32